#ifndef _AME_CHAIN_H_
#define _AME_CHAIN_H_

#define CHAIN_MILESTONES 32
#define MILE 255

// Push after
#define CHAIN_PUSHA(node, me) me->parent = node->parent;\
me->parent->len++;\
me->prev = node;\
me->next = node->next; \
node->next = me;\
if (node == node->parent->last)\
	node->parent->last = me;\
\
if (me->next) {\
	me->next->prev = me;\
}\
else {\
	me->parent->last = me;\
}

#define CHAIN_UNLINK(node) \
if (node->parent) {\
	if (node == node->parent->first)\
		node->parent->first = node->next;\
	if (node == node->parent->last)\
		node->parent->last = node->prev;\
	if (node->next)\
		node->next->prev = node->prev;\
	if (node->prev)\
		node->prev->next = node->next;\
	node->parent->len--;\
	node->parent = 0;\
}

// Push before
#define CHAIN_PUSHB(node, me) me->parent = node->parent; \
me->parent->len++;\
me->next = node; \
me->prev = node->prev; \
node->prev = me;\
if (me->prev)\
	me->prev->next = me; \
else {\
	me->parent->first = me;\
}

#define CHAIN_REM(node) if (node->parent) {\
if (node->parent->first == node->parent->last) \
	node->parent->first = node->parent->last = 0; \
else if (node->parent->first == node) \
	node->parent->first = node->next; \
else if (node->parent->last == node) \
	node->parent->last = node->prev; \
if (node->next) \
	node->next->prev = node->prev; \
if (node->prev) \
	node->prev->next = node->next;\
node->parent = 0;\
}

#define CHAIN_MOVE(node, set) \
	CHAIN_UNLINK(node); \
	CHAIN_PUSHA(set->last, node);

/*
#define CHAIN_POP(set, node) \
if (set) {\
	node = set->last;\
	if (node) {\
		if (node->prev)\
			node->prev->next = 0;\
		node->parent = 0; \
		set->last = node->prev;\
		set->len --;\
	}\
}\
else {\
	node = 0;\
}
*/

#define CHAIN_SHIFT(set, node) \
if (set) {\
	if (set->first) {\
		node = set->first;\
		set->first = node->next;\
		set->len--;\
	}\
	else {\
		node = set->first;\
		set->first = set->last = 0,set->len = 0;\
	}\
}

#define CHAIN_POP(set, node) \
if (set) {\
	if (set->last) {\
		node = set->last;\
		set->last = node->prev;\
		set->len--;\
	}\
	else {\
		node = set->first;\
		set->first = set->last = 0,set->len = 0;\
	}\
}

/*
#define CHAIN_SHIFT(set, node) \
if (set) { \
	node = set->first; \
	if (node) { \
		if (node->next) {\
			node->next->prev = 0;\
			set->first = node->next;\
		}\
		else {\
			set->first = set->last = 0; \
		}\
		node->parent = 0;\
		set->len --;\
	} \
} \
else { \
	node = 0; \
}
*/
#define chain_shift CHAIN_SHIFT
#define chain_pop CHAIN_POP

struct chain_node;
struct chain_set;

struct chain_node {
	struct chain_set *parent;
	struct chain_node *next;
	struct chain_node *prev;
	unsigned milestone: 1; 
	unsigned int index;
	void *val;
};

struct chain_set {
	unsigned int len;
	unsigned int maxlen;
	struct chain_node *first;
	struct chain_node *last;
	struct chain_node *milestones[CHAIN_MILESTONES];
	unsigned contiguous: 1;
	enum {
		CS_NONE,
		CS_CLIENTSCHAIN,
		CS_MAPSCHAIN,
		CS_MAPCLIENTSCHAIN,
		CS_MAPTIMERSCHAIN
	} type;
	void *val;
};

struct chain_node *chain_popF(struct chain_set *me);
struct chain_node *chain_shiftF(struct chain_set *me);
inline struct chain_node *chain_push(struct chain_set *set, void *val);
inline struct chain_node *chain_pushback(struct chain_set *set, void *val);
inline struct chain_node *chain_getNode(void);
struct chain_node *chain_insertUnique(struct chain_set *set, int pos, void *val, unsigned char replace);

struct chain_node *chain_findIndex (struct chain_set *parent, int pos);
inline struct chain_node *chain_findExactIndex (struct chain_set *parent, int pos);
inline struct chain_node *chain_swab (struct chain_node *a, struct chain_node *b);
inline struct chain_node *chain_findByVal(struct chain_set *, void *);

struct chain_node * chain_nodeInit(struct chain_set *, void *, unsigned int, struct chain_node *);
int chain_nodeShut(struct chain_node *);
int chain_nodeSetPos(struct chain_node*, unsigned short pos);
int chain_RemoveNodeByPos(struct chain_set *, unsigned short);

struct chain_set *chain_setInit(void *, int, int);
void chain_setClear(struct chain_set *);
void chain_setShut(struct chain_set *);
struct chain_set *chain_setInvert(struct chain_set *);
void chain_setShutFreePointers(struct chain_set *);
void _CHAIN_PUSHA(struct chain_node *node, struct chain_node *me);
#endif
